/**    
 * 文件名：AutoGenerator.java
 * 版本信息： v 1.0   
 * 日期：2016年6月22日  
 * Copyright 深圳市佰仟金融服务有限公司 2016    
 * 版权所有
 *    
 */
package com.easyweb.generator;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.aeonbits.owner.ConfigFactory;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.easyweb.generator.common.config.DBDialect;
import com.easyweb.generator.common.config.MysqlTypeMapping;
import com.easyweb.generator.common.config.SystemConfig;
import com.easyweb.generator.common.config.TemplateType;
import com.easyweb.generator.common.utils.FreemarkerTemplateHelper;
import com.easyweb.generator.common.utils.MyBatisQueryUtils;
import com.easyweb.generator.common.utils.StrKit;
import com.easyweb.generator.model.ColumnMeta;
import com.easyweb.generator.model.TableMeta;
import com.google.common.collect.Lists;

import freemarker.template.TemplateException;

/**
 * 自动生成代码
 * 
 * @ClassName: AutoGenerator
 * @author:WeiHui.Zhang
 * @date: 2016年6月22日 上午9:14:01
 * @version: v 1.0
 */
public class AutoGenerator {

	private static final Logger LOGGER = LoggerFactory.getLogger(AutoGenerator.class);
	private SystemConfig config;

	public AutoGenerator(SystemConfig config) {
		this.config = config;
	}

	public static void main(String[] args) {
		long begin = System.currentTimeMillis();
		LOGGER.info("---------------生成代码开始--------------");
		SystemConfig config = ConfigFactory.create(SystemConfig.class);
		mkdirs(config);

		new AutoGenerator(config).generatorOne("sys_user");

		long end = System.currentTimeMillis();
		LOGGER.info("---------------生成代码结束，耗时:{}ms!--------------", (end - begin));
	}

	private static void mkdirs(SystemConfig systemConfig) {
		File defaultOutPath = new File(systemConfig.defaultOutPath());
		defaultOutPath.mkdirs();

		File moduleOutPath = new File(systemConfig.moduleOutPath());
		moduleOutPath.mkdirs();

		File daoOutPath = new File(systemConfig.daoOutPath());
		daoOutPath.mkdirs();

		File serviceOutPath = new File(systemConfig.serviceOutPath());
		serviceOutPath.mkdirs();

		File mapperOutPath = new File(systemConfig.mapperOutPath());
		mapperOutPath.mkdirs();

		File controllerOutPath = new File(systemConfig.controllerOutPath());
		controllerOutPath.mkdirs();

		File serviceImplOutPath = new File(systemConfig.serviceImplOutPath());
		serviceImplOutPath.mkdirs();
	}

	public void generatorOne(String tableName) {
		TableMeta tableMeta = generatorCore(tableName, config.dialect());
		tableMeta.setModelName(buildModelName(tableName));
		generateAll(tableMeta);
	}

	public void generatorOne(String tableName, String javaClass) {
		// TODO 检查最后不要放这里，在初始化时就对config校验
		String dialect = config.dialect();
		if (StringUtils.isBlank(dialect)) {
			throw new IllegalArgumentException("请设置数据库方言！");
		}
		TableMeta tableMeta = generatorCore(tableName, dialect);
		tableMeta.setModelName(javaClass);

		generateAll(tableMeta);
	}

	private void generateAll(TableMeta tableMeta) {
		generateCode(tableMeta, TemplateType.Model, config.defaultOutPath());
		generateCode(tableMeta, TemplateType.Service, config.serviceOutPath());
		// generateCode(tableMeta, TemplateType.Mapper, config.mapperOutPath());
		// generateCode(tableMeta, TemplateType.Dao, config.daoOutPath());
		// generateCode(tableMeta, TemplateType.ServiceImpl,
		// config.serviceImplOutPath());
		// generateCode(tableMeta, TemplateType.Controller,
		// config.controllerOutPath());
		autoOpenDir();
	}

	private void generateCode(TableMeta tableMeta, TemplateType templateType, String fileOutPath) {
		try {
			/**
			 * 生成的 model 名
			 */
			String module = tableMeta.getModelName();
//			List<ColumnMeta> columnMetas = tableMeta.getColumnMetas();
//			String tableName = tableMeta.getName();
			/**
			 * 输入模版需要参数
			 */
			Map<String, Object> para = new HashMap<>();
			para.put("createdName", config.createdName());
			para.put("createdTime", SystemConfig.createdTime);
//			para.put("module", module);
//			para.put("columnMetas", columnMetas);
//			para.put("moduleTable", tableName);
			para.put("tableMeta", tableMeta);
			para.put("package", config.packageName());
//			para.put("moduleSeq", module + "_SEQ");
//			para.put("lowModule", module.substring(0, 1).toLowerCase() + module.substring(1));
			/**
			 * 生成输出文件名
			 */
			StringBuffer filePath = new StringBuffer(fileOutPath + File.separator);
			if (templateType.name().equals(TemplateType.Model.name())) {
				filePath.append(module);
			} else {
				filePath.append(module + templateType.name());
			}
			filePath.append(templateType.name().equals(TemplateType.Mapper.name()) ? ".xml" : ".java");

			/**
			 * 调用Freemarker模版引擎 传入模版名称 和参数 输出指定文件
			 */
			FreemarkerTemplateHelper.loadTemplateByFile(templateType.getCode(), para, filePath.toString());
		} catch (IOException e) {
			e.printStackTrace();
		} catch (TemplateException e) {
			e.printStackTrace();
		}
	}

	private TableMeta generatorCore(String tableName, String dialect) {
		TableMeta tableMeta = new TableMeta(tableName);
		List<ColumnMeta> columnMetas = Lists.newArrayList();
		/**
		 * mysql数据库
		 */
		if (DBDialect.MYSQL.name().equalsIgnoreCase(dialect)) {
			String jdbcUrl = SystemConfig.prop.get("jdbc.url");
			String dbName = StringUtils.substringAfterLast(jdbcUrl, "/");
			String statement = "GenerateCodeModuleMapper.findListMysql";// 映射sql的标识字符串
			Map<String, String> param = new HashMap<>();
			param.put("db", dbName);
			param.put("table", tableName);
			columnMetas = MyBatisQueryUtils.getInstance().getSessionFactory().openSession().selectList(statement,
					param);
			if (columnMetas != null && !columnMetas.isEmpty()) {
				for (ColumnMeta columnMeta : columnMetas) {
					String dbType = columnMeta.getType().toLowerCase();
					String javaType = MysqlTypeMapping.getJavaType(dbType);
					// 设置javaType
					columnMeta.setJavaType(javaType);
					// 设置属性名称
					String attrName = buildAttrName(columnMeta.getName());
					columnMeta.setAttrName(attrName);
					if ("Date".equals(javaType)) {
						tableMeta.setImportDate(true);
					}
				}
			}
		}
		if (DBDialect.ORACLE.name().equalsIgnoreCase(dialect)) {
			tableName = tableName.toUpperCase();
			String statement = "GenerateCodeModuleMapper.findList";// 映射sql的标识字符串
			columnMetas = MyBatisQueryUtils.getInstance().getSessionFactory().openSession().selectList(statement,
					tableName);
		}
		tableMeta.setColumnMetas(columnMetas);
		return tableMeta;
	}

	/**
	 * 构造 modelName，mysql 的 tableName 建议使用小写字母，多单词表名使用下划线分隔，不建议使用驼峰命名 oracle 之下的
	 * tableName 建议使用下划线分隔多单词名，无论 mysql还是 oralce，tableName 都不建议使用驼峰命名
	 */
	protected String buildModelName(String tableName) {
		// 移除表名前缀仅用于生成 modelName、baseModelName，而 tableMeta.name 表名自身不能受影响
		// if (removedTableNamePrefixes != null) {
		// for (String prefix : removedTableNamePrefixes) {
		// if (tableName.startsWith(prefix)) {
		// tableName = tableName.replaceFirst(prefix, "");
		// break;
		// }
		// }
		// }
		return StrKit.firstCharToUpperCase(StrKit.toCamelCase(tableName));
	}

	/**
	 * 构造 colName 所对应的 attrName，mysql 数据库建议使用小写字段名或者驼峰字段名 Oralce
	 * 反射将得到大写字段名，所以不建议使用驼峰命名，建议使用下划线分隔单词命名法
	 */
	protected String buildAttrName(String colName) {
		return StrKit.toCamelCase(colName);
	}

	public void generatorALL() {

	}

	/**
	 * 
	 * @Description:自动打开生成文件的目录
	 * @author: WeiHui.Zhang
	 * @date: 2016年6月22日 上午9:19:20
	 * @Version: V1.00
	 * @Title: autoOpenDir void
	 */
	public void autoOpenDir() {
		try {
			String osName = System.getProperty("os.name");
			if (osName != null) {
				if (osName.contains("Mac")) {
					Runtime.getRuntime().exec("open " + config.moduleOutPath());
				} else if (osName.contains("Windows")) {
					Runtime.getRuntime().exec("cmd /c start " + config.moduleOutPath());
				} else {
					System.err.println("save dir:" + config.moduleOutPath());
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
